﻿using codeparser.net.v2.Aspects;
using codeparser.net.v2.Contracts;
using codeparser.net.v2.Entities;

using System;
using System.Linq.Expressions;
using System.Xml;

namespace codeparser.net.v2.ContentTransformer
{
    /// <summary>
    /// Represents a content transformer.
    /// </summary>
    public class Transformer : IXmlWriter
    {
        /// <summary>
        /// Contains the next content transformer.
        /// </summary>
        private Transformer _nextContentTransformer;

        /// <summary>
        /// Contains the transformation expression.
        /// </summary>
        private Expression<Func<string, string>> _transformExpression;

        /// <summary>
        /// Contains the transformation function.
        /// </summary>
        private Func<string, string> _transformFunc;

        /// <summary>
        /// Contains the scope.
        /// </summary>
        private TransformerScope _scope;

        /// <summary>
        /// Adds the specified transformation function.
        /// </summary>
        /// <param name="transformFunc">The transformation function.</param>
        /// <returns>The next content transformer.</returns>
        [LoggingAspect]
        public Transformer Add(Expression<Func<string, string>> transformFunc)
        {
            return this.Add(transformFunc, TransformerScope.Parse);
        }

        /// <summary>
        /// Adds the specified transformation function.
        /// </summary>
        /// <param name="transformFunc">The transformation function.</param>
        /// <param name="scope">The scope.</param>
        /// <returns>The next content transformer.</returns>
        [LoggingAspect]
        public Transformer Add(Expression<Func<string, string>> transformFunc, TransformerScope scope)
        {
            // Create a new content transformer and add it to the chain.
            this._nextContentTransformer = new Transformer();
            this._transformExpression = transformFunc;
            this._transformFunc = transformFunc.Compile();
            this._scope = scope;
            return this._nextContentTransformer;
        }

        /// <summary>
        /// Transforms the specified text.
        /// </summary>
        /// <param name="text">The text.</param>
        /// <param name="contentStyle">The content style.</param>
        /// <returns>The transformed text.</returns>
        [LoggingAspect]
        public string Transform(string text, TagContentStyle contentStyle)
        {
            // Check whether this transformer has a transformation function.
            if (this._transformFunc == null)
            {
                return text;
            }

            // Transform the text.
            switch(this._scope)
            {
                case TransformerScope.Everywhere:
                    text = this._transformFunc(text);
                    break;
                case TransformerScope.Parse:
                    if (contentStyle == TagContentStyle.Parse)
                    {
                        text = this._transformFunc(text);
                    }
                    break;
                case TransformerScope.NoParse:
                    if (contentStyle == TagContentStyle.NoParse)
                    {
                        text = this._transformFunc(text);
                    }
                    break;
                case TransformerScope.None:
                    break;
                default:
                    break;
            }

            // If there is a next content transformer, run it.
            if (this._nextContentTransformer != null)
            {
                text = this._nextContentTransformer.Transform(text, contentStyle);
            }

            // Return the text to the caller.
            return text;
        }

        /// <summary>
        /// Saves the transformer to the specified XML writer.
        /// </summary>
        /// <param name="writer">The XML writer.</param>
        [LoggingAspect]
        void IXmlWriter.Save(XmlWriter writer)
        {
            // If there is no transform function set, return.
            if (this._transformFunc == null)
            {
                return;
            }

            // Save the transformer.
            writer.WriteStartElement("Transformer");
            {
                writer.WriteAttributeString("TransformFunc", this._transformExpression.ToString());
                writer.WriteAttributeString("Scope", this._scope.ToString());
            }
            writer.WriteEndElement();

            // Proceed to the next transformer.
            if (this._nextContentTransformer != null)
            {
                ((IXmlWriter)this._nextContentTransformer).Save(writer);
            }
        }
    }
}